package com.thirtydays.frame.ui.marquee

import android.content.Context
import android.graphics.Typeface
import android.os.Build
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.TextView
import android.widget.ViewFlipper
import androidx.annotation.AnimRes
import androidx.annotation.FontRes
import androidx.core.content.res.ResourcesCompat
import com.blankj.utilcode.util.SizeUtils
import com.thirtydays.frame.R

/**
 * @author 小垚
 * @创建时间： 2021/1/21
 * @描述： 跑马灯View
 **/

const val GRAVITY_LEFT = 0
const val GRAVITY_CENTER = 1
const val GRAVITY_RIGHT = 2

const val DIRECTION_BOTTOM_TO_TOP = 0
const val DIRECTION_TOP_TO_BOTTOM = 1
const val DIRECTION_RIGHT_TO_LEFT = 2
const val DIRECTION_LEFT_TO_RIGHT = 3

class MarqueeView : ViewFlipper {
    constructor(context: Context?) : super(context) {
        initView(context, null, 0)
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        initView(context, attrs, 0)
    }

    private var interval = 3000
    private var hasSetAnimDuration = false
    private var animDuration = 1000
    private var textSize = 14f
    private var textColor = -0x1000000
    private var singleLine = false

    private var gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL

    private var direction = DIRECTION_BOTTOM_TO_TOP

    private var typeface: Typeface? = null

    @AnimRes
    private var inAnimResId: Int = R.anim.anim_bottom_in

    @AnimRes
    private var outAnimResId: Int = R.anim.anim_top_out

    private var position = 0
    private var messages = mutableListOf<Any>()
    private var onItemClickListener: OnItemClickListener? = null


    private fun initView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) {
        val typedArray = context!!.obtainStyledAttributes(attrs, R.styleable.MarqueeViewStyle, defStyleAttr, 0)

        interval = typedArray.getInteger(R.styleable.MarqueeViewStyle_mvInterval, interval)
        hasSetAnimDuration = typedArray.hasValue(R.styleable.MarqueeViewStyle_mvAnimDuration)
        animDuration = typedArray.getInteger(R.styleable.MarqueeViewStyle_mvAnimDuration, animDuration)
        singleLine = typedArray.getBoolean(R.styleable.MarqueeViewStyle_mvSingleLine, false)
        if (typedArray.hasValue(R.styleable.MarqueeViewStyle_mvTextSize)) {
            textSize = typedArray.getDimension(R.styleable.MarqueeViewStyle_mvTextSize, textSize)
            textSize = SizeUtils.px2sp(textSize).toFloat()
        }
        textColor = typedArray.getColor(R.styleable.MarqueeViewStyle_mvTextColor, textColor)
        @FontRes val fontRes = typedArray.getResourceId(R.styleable.MarqueeViewStyle_mvFont, 0)
        if (fontRes != 0) {
            typeface = ResourcesCompat.getFont(context, fontRes)
        }

        when (typedArray.getInt(R.styleable.MarqueeViewStyle_mvGravity, GRAVITY_LEFT)) {
            GRAVITY_LEFT -> gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
            GRAVITY_CENTER -> gravity = Gravity.CENTER
            GRAVITY_RIGHT -> gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
        }

        if (typedArray.hasValue(R.styleable.MarqueeViewStyle_mvDirection)) {
            direction = typedArray.getInt(R.styleable.MarqueeViewStyle_mvDirection, direction)
            when (direction) {
                DIRECTION_BOTTOM_TO_TOP -> {
                    inAnimResId = R.anim.anim_bottom_in
                    outAnimResId = R.anim.anim_top_out
                }
                DIRECTION_TOP_TO_BOTTOM -> {
                    inAnimResId = R.anim.anim_top_in
                    outAnimResId = R.anim.anim_bottom_out
                }
                DIRECTION_RIGHT_TO_LEFT -> {
                    inAnimResId = R.anim.anim_right_in
                    outAnimResId = R.anim.anim_left_out
                }
                DIRECTION_LEFT_TO_RIGHT -> {
                    inAnimResId = R.anim.anim_left_in
                    outAnimResId = R.anim.anim_right_out
                }
            }
        } else {
            inAnimResId = R.anim.anim_bottom_in
            outAnimResId = R.anim.anim_top_out
        }

        typedArray.recycle()
        flipInterval = interval
    }

    /**
     * 根据字符串，启动翻页公告
     *
     * @param message 字符串
     */
    fun startWithText(message: String) {
        startWithText(message, inAnimResId, outAnimResId)
    }

    /**
     * 根据字符串，启动翻页公告
     *
     * @param message      字符串
     * @param inAnimResId  进入动画的resID
     * @param outAnimResID 离开动画的resID
     */
    fun startWithText(message: String, @AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        if (TextUtils.isEmpty(message)) {
            return
        }
        viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
                } else {
                    viewTreeObserver.removeGlobalOnLayoutListener(this)
                }
                startWithFixedWidth(message, inAnimResId, outAnimResID)
            }
        })
    }

    /**
     * 根据字符串和宽度，启动翻页公告
     *
     * @param message 字符串
     */
    private fun startWithFixedWidth(message: String, @AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        val messageLength = message.length
        val width = SizeUtils.px2dp(width.toFloat())
        if (width == 0) {
            throw RuntimeException("Please set the width of MarqueeView !")
        }
        val limit = (width / textSize).toInt()
        val list = mutableListOf<Any>()
        if (messageLength <= limit) {
            list.add(message as Nothing)
        } else {
            val size = messageLength / limit + if (messageLength % limit != 0) 1 else 0
            for (i in 0 until size) {
                val startIndex = i * limit
                val endIndex = if ((i + 1) * limit >= messageLength) messageLength else (i + 1) * limit
                list.add(message.substring(startIndex, endIndex) as Nothing)
            }
        }
        if (messages == null) {
            messages = mutableListOf()
        }
        messages.clear()
        messages.addAll(list)
        postStart(inAnimResId, outAnimResID)
    }

    /**
     * 根据字符串列表，启动翻页公告
     *
     * @param messages 字符串列表
     */
    fun startWithList(messages: MutableList<Any>) {
        startWithList(messages, inAnimResId, outAnimResId)
    }

    /**
     * 根据字符串列表，启动翻页公告
     *
     * @param messages     字符串列表
     * @param inAnimResId  进入动画的resID
     * @param outAnimResID 离开动画的resID
     */
    fun startWithList(messages: MutableList<Any>, @AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        if (messages.isEmpty()) {
            return
        }
        this.messages = messages
        postStart(inAnimResId, outAnimResID)
    }

    private fun postStart(@AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        post { start(inAnimResId, outAnimResID) }
    }

    private var isAnimStart = false

    private fun start(@AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        removeAllViews()
        clearAnimation()
        // 检测数据源
        if (messages.isNullOrEmpty()) {
            throw RuntimeException("The messages cannot be empty!")
        }
        position = 0
        addView(createTextView(messages[position]!!))
        if (messages.size > 1) {
            setInAndOutAnimation(inAnimResId, outAnimResID)
            startFlipping()
        }
        if (inAnimation != null) {
            inAnimation.setAnimationListener(object : Animation.AnimationListener {
                override fun onAnimationStart(animation: Animation) {
                    if (isAnimStart) {
                        animation.cancel()
                    }
                    isAnimStart = true
                }

                override fun onAnimationEnd(animation: Animation) {
                    position++
                    if (position >= messages.size) {
                        position = 0
                    }
                    val view: View = createTextView(messages[position]!!)
                    if (view.parent == null) {
                        addView(view)
                    }
                    isAnimStart = false
                }

                override fun onAnimationRepeat(animation: Animation) {}
            })
        }
    }

    private fun createTextView(marqueeItem: Any): TextView {
        var textView = getChildAt((displayedChild + 1) % 3) as TextView
        if (textView == null) {
            textView = TextView(context)
            textView.gravity = gravity or Gravity.CENTER_VERTICAL
            textView.setTextColor(textColor)
            textView.textSize = textSize
            textView.includeFontPadding = true
            textView.isSingleLine = singleLine
            if (singleLine) {
                textView.maxLines = 1
                textView.ellipsize = TextUtils.TruncateAt.END
            }
            if (typeface != null) {
                textView.typeface = typeface
            }
            textView.setOnClickListener { v ->
                if (onItemClickListener != null) {
                    onItemClickListener!!.onItemClick(currentView.tag as Int, v as TextView)
                }
            }
        }
        var message: CharSequence? = ""
        if (marqueeItem is CharSequence) {
            message = marqueeItem
        } else if (marqueeItem is IMarqueeItem) {
            message = marqueeItem.marqueeMessage()
        }
        textView.text = message
        textView.tag = position
        return textView
    }

    /**
     * 设置进入动画和离开动画
     *
     * @param inAnimResId  进入动画的resID
     * @param outAnimResID 离开动画的resID
     */
    private fun setInAndOutAnimation(@AnimRes inAnimResId: Int, @AnimRes outAnimResID: Int) {
        val inAnim = AnimationUtils.loadAnimation(context, inAnimResId)
        if (hasSetAnimDuration) {
            inAnim.duration = animDuration.toLong()
        }
        inAnimation = inAnim
        val outAnim = AnimationUtils.loadAnimation(context, outAnimResID)
        if (hasSetAnimDuration) {
            outAnim.duration = animDuration.toLong()
        }
        outAnimation = outAnim
    }

    fun setTypeface(typeface: Typeface?) {
        this.typeface = typeface
    }
}